package de.bremen.bremenbybike;

import java.util.ArrayList;

import com.actionbarsherlock.app.SherlockFragment;

import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

public class MapFragment extends SherlockFragment {

	/**
	 * the visible webview.
	 */
	private WebView webview = null;

	/**
	 * that fill represent the loading state of the webview.
	 */
	private boolean loadingInProgress = true;

	/**
	 * stored javascript actions that was executeded during page loading.
	 */
	private ArrayList<String> javaScriptActions = new ArrayList<String>();

	@Override
	public View onCreateView(final LayoutInflater inflater,
			final ViewGroup container, final Bundle savedInstanceState) {
		// the multitouch MotionEvents needs API level 5 as minimum.
		webview = new WebView(getActivity().getApplicationContext()) {

			// implement own pinch-zoom for the Google Map on webview

			/**
			 * store the current state, if pinch-zoom is in action.
			 */
			private boolean duringZoom = false;

			/**
			 * store the distance at the start of pinch-zoom.
			 */
			private float oldDist;

			/**
			 * use this minimum distance as error correction.
			 */
			private static final float MIN_PINCH_DISTANCE = 10f;

			@Override
			public boolean onTouchEvent(final MotionEvent ev) {
				switch (ev.getAction() & MotionEvent.ACTION_MASK) {
				case MotionEvent.ACTION_UP:
				case MotionEvent.ACTION_POINTER_UP:
					duringZoom = false;
					executeJsAction("stopPinchZoom()");
					break;
				case MotionEvent.ACTION_POINTER_DOWN:
					oldDist = spacing(ev);
					if (oldDist > MIN_PINCH_DISTANCE) {
						duringZoom = true;
						executeJsAction("startPinchZoom()");
					}
					break;
				case MotionEvent.ACTION_MOVE:
					if (duringZoom) {
						float newDist = spacing(ev);
						if (newDist > MIN_PINCH_DISTANCE) {
							float scale = newDist / oldDist;
							executeJsAction("updatePinchZoom(" + scale
									+ ")");
						}
					}
					break;
				default:
					// nothing to do.
				}
				return super.onTouchEvent(ev);
			}

			/**
			 * calculate the spacing between two touch points during
			 * multitouch.
			 * 
			 * @param event
			 *            touch event that contains all informations
			 * @return the spacing
			 */
			private float spacing(final MotionEvent event) {
				float x = event.getX(0) - event.getX(1);
				float y = event.getY(0) - event.getY(1);
				return FloatMath.sqrt(x * x + y * y);
			}
		};
		String appVersionName;
		try {
			appVersionName = getActivity().getPackageManager().getPackageInfo(
					getActivity().getPackageName(), 0).versionName;
		} catch (NameNotFoundException e) {
			appVersionName = "??";
		}
		final String userAgent = Const.UA_APPVERSION + appVersionName
				+ Const.UA_SEPERATOR + Const.UA_ANDROIDVERSION
				+ Build.VERSION.RELEASE + Const.UA_SEPERATOR + Build.MANUFACTURER
				+ Const.UA_SEPERATOR + Build.DEVICE + Const.UA_SEPERATOR + Build.MODEL;

		final WebSettings settings = webview.getSettings();
		settings.setUserAgentString(userAgent);
		settings.setJavaScriptEnabled(true);
		settings.setCacheMode(WebSettings.LOAD_NORMAL);
		settings.setBuiltInZoomControls(false);
		settings.setSupportZoom(false);
		webview.addJavascriptInterface(new JavaScriptInteraction(getActivity()
				.getApplicationContext()), Const.JAVASCRIP_TINTERFACE_NAME);
		// webview.setWebViewClient(new WebViewClient() {
		// TODO handle some errors, the loading state, ...
		// });
		webview.loadUrl(Const.MAP_URL);
		webview.setOnKeyListener(new OnKeyListener() {

			@Override
			public boolean onKey(final View v, final int keyCode,
					final KeyEvent event) {
				switch (keyCode) {
				case KeyEvent.KEYCODE_ZOOM_IN:
					executeJsAction("zoomIn()");
					break;
				case KeyEvent.KEYCODE_ZOOM_OUT:
					executeJsAction("zoomOut()");
					break;
				case KeyEvent.KEYCODE_W:
					executeJsAction("moveViewTop()");
					break;
				case KeyEvent.KEYCODE_A:
					executeJsAction("moveViewLeft()");
					break;
				case KeyEvent.KEYCODE_S:
					executeJsAction("moveViewBottom()");
					break;
				case KeyEvent.KEYCODE_D:
					executeJsAction("moveViewRight()");
					break;
				default:
					return false;
				}
				return true;
			}
		});
		return webview;
	}

	/**
	 * interface for communicate with the JavaScript code.
	 * 
	 * @author Jan Stalhut
	 */
	private class JavaScriptInteraction {
		/**
		 * current application context.
		 */
		private final Context context;

		/**
		 * creates a interface to interact with the JavaScript code on the
		 * webview with Google Maps.
		 * 
		 * @param pContext
		 *            current application context
		 */
		public JavaScriptInteraction(final Context pContext) {
			this.context = pContext;
		}

		/**
		 * callback function that will called by JavaScript when all loading
		 * processes are completed.
		 */
		@SuppressWarnings("unused")
		public void loadingFinished() {
			Log.i("JS", "called loadingFinished()");
			loadingInProgress = false;
			javaScriptActions.add("showRoute([3485513.5,5880338,3485514.15277,5880334.66361,-1,-1,3485513.5,5880338,3485514.5,5880360,3485518.5,5880380,3485524.5,5880396,3485534.75,5880410.5,3485546.25,5880426.5,3485567,5880446.5,3485592.25,5880471,-1,-1,3485592.25,5880471,3485629,5880503.5,3485663.75,5880534.5,3485691.25,5880562,3485713.75,5880582,3485729,5880596.5,-1,-1,3485729,5880596.5,3485709.5,5880670,3485696,5880729.5,3485676.5,5880812,-1,-1,3485676.5,5880812,3485671.75,5880832.5,3485667,5880852.5,3485660.25,5880887,-1,-1,3485660,5880991.5,3485656.75,5880984.5,3485655.75,5880978.5,3485654.5,5880972.5,3485654.5,5880961.5,3485655.25,5880958,3485655.75,5880945.5,3485655.5,5880922.5,3485660.25,5880887,-1,-1,3485660,5880991.5,3485664.25,5880999.5,3485668.75,5881004,3485676,5881011.5,3485737.5,5881070.5,3485815.5,5881151,3485853.75,5881191,-1,-1,3485853.75,5881191,3485844.25,5881205,3485825.5,5881228,3485801,5881260.5,3485769.25,5881302,3485737.5,5881343,3485713.25,5881374.5,3485682,5881410.5,3485647.75,5881449.5,3485643,5881455,3485617.75,5881484,3485587.25,5881517.5,-1,-1,3485587.25,5881517.5,3485561.75,5881547,3485531.75,5881581,3485528,5881585.5,-1,-1,3485528,5881585.5,3485508.25,5881608,-1,-1,3485508.25,5881608,3485474.5,5881644,-1,-1,3485474.5,5881644,3485461,5881657.5,-1,-1,3485461,5881657.5,3485461,5881671,3485454.5,5881683.5,3485447.75,5881692.5,-1,-1,3485447.75,5881692.5,3485432.25,5881708.5,3485418.25,5881726,-1,-1,3485418.25,5881726,3485405.75,5881738.5,3485392.25,5881754,3485382,5881770.5,3485381,5881786,3485383.5,5881801.5,-1,-1,3485383.5,5881801.5,3485465,5881879.5,3485510.5,5881923,3485548.25,5881957.5,-1,-1,3485548.25,5881957.5,3485608.25,5882016.5,3485667,5882072.5,3485697.75,5882103.5,-1,-1,3485697.75,5882103.5,3485739.5,5882147,3485758.75,5882167.5,-1,-1,3485758.75,5882167.5,3485734.25,5882194,3485712.5,5882217,-1,-1,3485712.5,5882217,3485678.75,5882252,-1,-1,3485678.75,5882252,3485692,5882264.5,3485699.25,5882273,3485702.5,5882276.5,3485706.75,5882283.5,3485717.5,5882305,3485736.75,5882338,3485745,5882354,3485749.5,5882361,-1,-1,3485749.5,5882361,3485741.25,5882369,3485727,5882381.5,-1,-1,3485727,5882381.5,3485733.5,5882395.5,3485754,5882436.5,3485769,5882463.5,3485778.75,5882479.5,-1,-1,3485778.75,5882479.5,3485766.75,5882488.5,3485764,5882490.5,3485761.25,5882493,3485748,5882503,-1,-1,3485748,5882503,3485700.75,5882537.5,-1,-1,3485700.75,5882537.5,3485689.75,5882546,3485676.5,5882556.5,3485653.25,5882574.5,3485645,5882581,3485630.5,5882592,3485616,5882603,3485605.5,5882613,-1,-1,3485605.5,5882613,3485577.5,5882607,3485563,5882605.5,3485541,5882605.5,3485517,5882608.5,3485490.25,5882612.5,3485471,5882617,-1,-1,3485471,5882617,3485455.75,5882619,3485442.5,5882622,3485420.25,5882628,3485396.75,5882636.5,3485373.25,5882644.5,3485352,5882649,-1,-1,3485352,5882649,3485334.75,5882654,3485320,5882658.5,-1,-1,3485320,5882658.5,3485303,5882663,3485275.75,5882669.5,3485240.75,5882679,3485219,5882686,-1,-1,3485219,5882686,3485192.75,5882693,3485156.5,5882705.5,3485123,5882717,3485094.5,5882728,3485047.25,5882744,3485019.75,5882756.5,-1,-1,3485019.75,5882756.5,3484988.75,5882804,3484978.5,5882816,-1,-1,3484978.5,5882816,3484942.75,5882846,-1,-1,3484942.75,5882846,3484954.76446,5882866.655,-1,-1,-1,-1])");
			for (final String action : javaScriptActions) {
				executeJsAction(action);
			}
		}

		/**
		 * shows a message as Toast on Android.
		 * 
		 * @param text
		 *            message text.
		 * @param longTime
		 *            show the toast longer.
		 */
		public void showMessage(final String text, final boolean longTime) {
			Toast.makeText(context, text,
					longTime ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
		}

		/**
		 * shows a short message as Toast on Android.
		 * 
		 * @param text
		 *            message text.
		 */
		@SuppressWarnings("unused")
		public void showMessage(final String text) {
			showMessage(text, false);
		}

	}

	/**
	 * executes the given JavaScript code on webview. Stores the commands, if
	 * the webview is not ready.
	 * 
	 * @param action
	 *            JavaScript code.
	 */
	private void executeJsAction(final String action) {
		if (loadingInProgress) {
			javaScriptActions.add(action);
		} else {
			Log.i("JS", "call JS-function: " + action);
			webview.loadUrl(Const.JAVASCRIPT_URL + action);
		}
	}
}
